#include "EControllerQtProperty.h"
#include "DefineCommon.h"
#include "ECommand.h"
#include "EItem.h"
#include "EItemManager.h"
#include "EItemMeta.h"
#include "EItemMetaFunction.h"
#include "EItemMetaProperty.h"
#include "EString.h"
#include "EVariant.h"
#include "WidgetVariant.h"
#include <WidgetVariantBool.h>
#include <WidgetVariantColor.h>
#include <WidgetVariantDouble.h>
#include <WidgetVariantInt.h>
#include <WidgetVariantMatrix.h>
#include <WidgetVariantString.h>
#include <WidgetVariantVec2.h>
#include <WidgetVariantVec3.h>
#include <WidgetVariantVec4.h>
#include <WidgetVariantButton.h>
#include "qobjectdefs.h"
#include "qtreewidget.h"
#include <ECommandManager.h>
#include <cstddef>
#include <cstdint>



EControllerQtProperty::EControllerQtProperty()
{
    this->addCmdSenderList(this->getTag());
}

EControllerQtProperty::~EControllerQtProperty()
{
}

void EControllerQtProperty::setWidgetBox(WidgetBox* w)
{
    this->m_widgetBox = w;
}

void EControllerQtProperty::refresh()
{
}

bool EControllerQtProperty::handleEItem(EItemPtr item, int depth, EItemPtr parentItem)
{
    if(item.get() == nullptr)
    {
        return true;
    }
    ELOG_DEBUG("selectedItem.name=%s", item->getName().data());

    this->m_itemPtr = item;
    this->m_mapTag2Property.clear();
    this->m_mapTag2Function.clear();
    this->m_mapTag2WidgetVariant.clear();
    this->m_widgetBox->clear();

    EItemMetaPtr meta = item->getOrCreatMeta();
    meta->getClassName();
    meta->getClassDescription();

    //convert property to widget
    for(size_t i =0 ; i < meta->getPropertyCount(); i++ )
    {
        EItemMetaPropertyPtr prop = meta->getProperty(i);
        QString groupName = prop->groupName.data();
        WidgetVariant* wPtr = this->convertPropertyToWidget(prop);
        if(wPtr != NULL)
        {
            this->m_widgetBox->addWidgetAndCategoryNew(wPtr, groupName);
            this->m_mapTag2WidgetVariant[prop->getTag()] = wPtr;
            this->m_mapTag2Property[prop->getTag()] = prop;
        }
    }

    //convert metaFunction to WidgetVariantButton
    for(size_t i =0 ; i < meta->getFunctionCount(); i++ )
    {
        EItemMetaFunctionPtr fun = meta->getFunction(i);
        QString groupName = fun->getGroupName().data();
        WidgetVariantButton* wPtr = new WidgetVariantButton;
        wPtr->refreshByFunction(fun);
        this->m_widgetBox->addWidgetAndCategoryNew(wPtr, groupName);
        this->m_mapTag2WidgetVariant[fun->getTag()] = wPtr;
        this->m_mapTag2Function[fun->getTag()] = fun;
    }

    this->m_widgetBox->installEventFilterForChildren();
    
    return true;
}

void EControllerQtProperty::handleCommand(const ECommandPtr& command)
{
    EString name = command->getName();
    ECommand::STATE state = command->getState();
    EString senderName = command->getSenderName();
    
    // ELOG_DEBUG("cmd.name=%s", command->getName().data());
    if(command->getName() == "c:selectItem" 
            && command->getState() == ECommand::STATE::NORMAL
            && command->getSenderName() == "WidgetTreeModel"
            )
    {
        this->handleCommandSelectItem(command);
    }
    else if(command->getName() == "c:propertyChanged" 
            && command->getState() == ECommand::STATE::UNDO
            // && command->getSenderName() == this->getName()
             )
    {
        this->handleCommandPropertyUndo(command);
    }
    else if(command->getName() == "c:propertyChanged" 
            && command->getState() == ECommand::STATE::REDO
            // && command->getSenderName() == this->getName()
            )
    {
        this->handleCommandPropertyRedo(command);
    }
    else if(command->getName() == "c:propertyChanged" 
            && command->getState() == ECommand::STATE::NORMAL
            && command->getSenderName() != this->getName()
            )
    {
        this->handleCommandPropertyChangedByOther(command);
    }
}

void EControllerQtProperty::handleCommandSelectItem(const ECommandPtr& command)
{
    EItemPtr selectedItem = command->getItemAdditional();
    this->handleEItem(selectedItem, -1, nullptr);
}

void EControllerQtProperty::handleCommandPropertyUndo(const ECommandPtr& command)
{
    EString propName = command->getOrCreatMeta()->getProperty("propName")->value.to_EString();
    long tagItem = command->getOrCreatMeta()->getProperty("tagItem")->value.to_long();
    long tagProperty = command->getOrCreatMeta()->getProperty("tagProperty")->value.to_long();
    EVariant oldValue = command->getOrCreatMeta()->getProperty("oldValue")->value;

    //if command's item is current item
    if(tagItem == this->m_itemPtr->getTag())
    {
        EItemMetaPropertyPtr p = this->m_itemPtr->getOrCreatMeta()->getProperty(propName);
        //set item's property value
        p->setPropertyValue(oldValue);
        //change Widget's value
        this->m_mapTag2WidgetVariant[tagProperty]->refreshByProperty(p);
    }
    else 
    {
        //if command's item is not current item
        EItemPtr item = EItemManager::instance()->getItemByTag(
                EItemManager::instance()->getItemTreeRoot()
                , tagItem
                );
        if(item != nullptr)
        {
            EItemMetaPropertyPtr p = item->getOrCreatMeta()->getProperty(propName);
            //set item's property value
            p->setPropertyValue(oldValue);
        }
    }

    ELOG_DEBUG("UNDO cmd.name=%s \n propName=%s \noldValue=%s \nnewValue=%s"
            , command->getName().data()
            , propName.data()
            , command->getOrCreatMeta()->getProperty("oldValue")->value.getPrintStr().data()
            , command->getOrCreatMeta()->getProperty("newValue")->value.getPrintStr().data()
            );
}

void EControllerQtProperty::handleCommandPropertyRedo(const ECommandPtr& command)
{
    EString propName = command->getOrCreatMeta()->getProperty("propName")->value.to_EString();
    long tagItem = command->getOrCreatMeta()->getProperty("tagItem")->value.to_intptr_t();
    long tagProperty = command->getOrCreatMeta()->getProperty("tagProperty")->value.to_intptr_t();
    EVariant newValue = command->getOrCreatMeta()->getProperty("newValue")->value;

    //if command's item is current item
    if(tagItem == this->m_itemPtr->getTag())
    {
        EItemMetaPropertyPtr p = this->m_itemPtr->getOrCreatMeta()->getProperty(propName);
        //set item's property value
        p->setPropertyValue(newValue);
        //change Widget's value
        this->m_mapTag2WidgetVariant[tagProperty]->refreshByProperty(p);
    }
    else 
    {
        //if command's item is not current item
        EItemPtr item = EItemManager::instance()->getItemByTag(
                EItemManager::instance()->getItemTreeRoot()
                , tagItem
                );
        if(item != nullptr)
        {
            EItemMetaPropertyPtr p = item->getOrCreatMeta()->getProperty(propName);
            //set item's property value
            p->setPropertyValue(newValue);
        }
    }
    ELOG_DEBUG("REDO cmd.name=%s \n propName=%s \noldValue=%s \nnewValue=%s"
            , command->getName().data()
            , propName.data()
            , command->getOrCreatMeta()->getProperty("oldValue")->value.getPrintStr().data()
            , command->getOrCreatMeta()->getProperty("newValue")->value.getPrintStr().data()
            );
}

void EControllerQtProperty::handleCommandPropertyChangedByOther(const ECommandPtr& command)
{
    EString propName = command->getOrCreatMeta()->getProperty("propName")->value.to_EString();
    long tagItem = command->getOrCreatMeta()->getProperty("tagItem")->value.to_long();
    long tagProperty = command->getOrCreatMeta()->getProperty("tagProperty")->value.to_long();
    EVariant oldValue = command->getOrCreatMeta()->getProperty("oldValue")->value;

    //if command's item is current item
    if(tagItem == this->m_itemPtr->getTag())
    {
        EItemMetaPropertyPtr p = this->m_itemPtr->getOrCreatMeta()->getProperty(propName);
        //change Widget's value
        this->m_mapTag2WidgetVariant[tagProperty]->refreshByProperty(p);
    }

}

WidgetVariant* EControllerQtProperty::convertPropertyToWidget(EItemMetaPropertyPtr p)
{
    WidgetVariant* wPtr = NULL;

    if(p->typeName == "int")
    {
        wPtr = new WidgetVariantInt;
        wPtr->refreshByProperty(p);
    }
    else if(p->typeName == "double")
    {
        wPtr = new WidgetVariantDouble;
        wPtr->refreshByProperty(p);
    }
    else if(p->typeName == "bool")
    {
        wPtr = new WidgetVariantBool;
        wPtr->refreshByProperty(p);
    }
    else if(p->typeName == "EVec3")
    {
        wPtr = new WidgetVariantVec3;
        wPtr->refreshByProperty(p);
    }
    else if(p->typeName == "EVec4")
    {
        wPtr = new WidgetVariantVec4;
        wPtr->refreshByProperty(p);
    }
    else if(p->typeName == "EColor")
    {
        wPtr = new WidgetVariantColor;
        wPtr->refreshByProperty(p);
    }
    else if(p->typeName == "EMatrix4x4")
    {
        wPtr = new WidgetVariantMatrix;
        wPtr->refreshByProperty(p);
    }
    else if(p->typeName == "EString")
    {
        wPtr = new WidgetVariantString;
        wPtr->refreshByProperty(p);
    }

    if(wPtr != NULL)
    {
        QObject::connect(wPtr
                , SIGNAL(signalVariantChanged(EItemMetaPropertyPtr, EVariant ))
                , this
                , SLOT(slotVariantChanged(EItemMetaPropertyPtr, EVariant))
                );
    }

    return wPtr;
}

void EControllerQtProperty::slotVariantChanged(EItemMetaPropertyPtr p, EVariant oldValue )
{
    ECommandPtr newCmd = NewSmartPtr(ECommand)();
    newCmd->setSender( this->getTag() );
    newCmd->setSenderName(this->getName());
    newCmd->setName("c:propertyChanged");
    newCmd->setSenderName(this->getName());
    newCmd->getOrCreatMeta()->addProperty("newValue", p->value);
    newCmd->getOrCreatMeta()->addProperty("oldValue", oldValue);
    newCmd->getOrCreatMeta()->addProperty("tagProperty", EVariant(p->getTag()));
    newCmd->getOrCreatMeta()->addProperty("tagItem", EVariant(this->m_itemPtr->getTag()));
    newCmd->getOrCreatMeta()->addProperty("propName", p->name);
    ECommandManager::instance()->pushECommand(newCmd);

    // ELOG_DEBUG("property.name=%s", p->name.data());
}
